﻿namespace WatiNSahi.Extensions
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;

    using global::WatiN.Core;
    using global::WatiN.Core.Constraints;
    
    using List = global::WatiN.Core.List;

    public static class ElementContainerExtensions
    {
        /// <summary>
        /// Gets TextField text
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="constraint">Constraint by which TextField will be searched</param>
        /// <returns>TextField text</returns>
        public static string GetText(this IElementContainer container, Constraint constraint)
        {
            var element = container.Get<TextField>(constraint);
            return element.Text;
        }

        public static string GetText(this IElementContainer container, Constraint constraint, bool assert)
        {
            var element = container.Get<TextField>(constraint, assert);
            return element.Text;
        }

        /// <summary>
        /// Gets TextField text
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldIdOrNameOrValueOrClassOrLabel">ID/Name/Value by which TextField will be searched</param>
        /// <returns>TextField text</returns>
        public static string GetText(this IElementContainer container, string textFieldIdOrNameOrValueOrClassOrLabel)
        {
            var element = container.Get<TextField>(textFieldIdOrNameOrValueOrClassOrLabel);
            return element.Text;
        }

        public static string GetText(this IElementContainer container, string textFieldIdOrNameOrValueOrClassOrLabel, bool assert)
        {
            var element = container.Get<TextField>(textFieldIdOrNameOrValueOrClassOrLabel, assert);
            return element.Text;
        }

        public static TElement Get<TElement>(this IElementContainer container, Constraint constraint) where TElement : Element
        {
            return container.Get<TElement>(constraint, ExtensionsSettings.Assert);
        }

        public static TElement Get<TElement>(this IElementContainer container, Constraint constraint, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(constraint);
            if (assert)
            {
                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' was not found using constraint '{1}'", typeof(TElement).Name, constraint));
            }

            return element;
        }

        public static TElement Get<TElement>(this IElementContainer container, string elementIdOrNameOrValueOrClassOrLabel) where TElement : Element
        {
            return container.Get<TElement>(elementIdOrNameOrValueOrClassOrLabel, ExtensionsSettings.Assert);
        }

        public static TElement Get<TElement>(this IElementContainer container, string elementIdOrNameOrValueOrClassOrLabel, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.ById(elementIdOrNameOrValueOrClassOrLabel)
                .Or(Find.ByName(elementIdOrNameOrValueOrClassOrLabel)
                .Or(Find.ByValue(elementIdOrNameOrValueOrClassOrLabel))
                .Or(Find.ByClass(elementIdOrNameOrValueOrClassOrLabel))
                .Or(Find.ByLabelText(elementIdOrNameOrValueOrClassOrLabel))));
            if (assert)
            {
                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with IdOrNameOrValue '{1}' was not found", typeof(TElement).Name, elementIdOrNameOrValueOrClassOrLabel));
            }

            return element;
        }

        public static TElement GetByAttribute<TElement>(this IElementContainer container, string elementAttributeName, string elementAttributeValue) where TElement : Element
        {
            return container.GetByAttribute<TElement>(elementAttributeName, elementAttributeValue, ExtensionsSettings.Assert);
        }

        public static TElement GetByAttribute<TElement>(this IElementContainer container, string elementAttributeName, string elementAttributeValue, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.By(elementAttributeName, elementAttributeValue));
            if (assert)
            {

                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with attribute name '{1}' and attribute value '{2}' was not found", typeof(TElement).Name, elementAttributeName, elementAttributeValue));

            }

            return element;
        }

        public static TElement GetByClass<TElement>(this IElementContainer container, string elementClass) where TElement : Element
        {
            return container.GetByClass<TElement>(elementClass, ExtensionsSettings.Assert);
        }

        public static TElement GetByClass<TElement>(this IElementContainer container, string elementClass, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.ByClass(elementClass));
            if (assert)
            {

                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with class '{1}' was not found", typeof(TElement).Name, elementClass));

            }

            return element;
        }

        public static TElement GetByText<TElement>(this IElementContainer container, string elementText) where TElement : Element
        {
            return container.GetByText<TElement>(elementText, ExtensionsSettings.Assert);
        }

        public static TElement GetByText<TElement>(this IElementContainer container, string elementText, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.ByText(elementText));
            if (assert)
            {

                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with text '{1}' was not found", typeof(TElement).Name, elementText));

            }

            return element;
        }

        public static TElement GetByUrl<TElement>(this IElementContainer container, string elementUrl) where TElement : Element
        {
            return container.GetByUrl<TElement>(elementUrl, ExtensionsSettings.Assert);
        }

        public static TElement GetByUrl<TElement>(this IElementContainer container, string elementUrl, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.ByUrl(elementUrl));
            if (assert)
            {

                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with url '{1}' was not found", typeof(TElement).Name, elementUrl));

            }

            return element;
        }

        public static TElement GetByUrl<TElement>(this IElementContainer container, Uri uri) where TElement : Element
        {
            return container.GetByUrl<TElement>(uri, ExtensionsSettings.Assert);
        }

        public static TElement GetByUrl<TElement>(this IElementContainer container, Uri uri, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.ByUrl(uri));
            if (assert)
            {

                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with url '{1}' was not found", typeof(TElement).Name, uri));

            }

            return element;
        }

        public static TElement GetByLabel<TElement>(this IElementContainer container, string elementText) where TElement : Element
        {
            return container.GetByLabel<TElement>(elementText, ExtensionsSettings.Assert);
        }

        public static TElement GetByLabel<TElement>(this IElementContainer container, string elementText, bool assert) where TElement : Element
        {
            var element = container.GetElementByConstraint<TElement>(Find.ByLabelText(elementText));
            if (assert)
            {

                Assert.IsTrue(element.Exists, string.Format("Element of type '{0}' with text '{1}' was not found", typeof(TElement).Name, elementText));

            }

            return element;
        }

        public static IList<TElement> GetAll<TElement>(this IElementContainer container, string elementIdOrNameOrValueOrClassOrLabel) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(Find.ById(elementIdOrNameOrValueOrClassOrLabel)
                .Or(Find.ByName(elementIdOrNameOrValueOrClassOrLabel)
                .Or(Find.ByValue(elementIdOrNameOrValueOrClassOrLabel))
                .Or(Find.ByClass(elementIdOrNameOrValueOrClassOrLabel))
                .Or(Find.ByLabelText(elementIdOrNameOrValueOrClassOrLabel))));
        }

        public static IList<TElement> GetAll<TElement>(this IElementContainer container, Constraint constraint) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(constraint);
        }

        public static IList<TElement> GetAllByAttribute<TElement>(this IElementContainer container, string elementAttributeName, string elementAttributeValue) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(Find.By(elementAttributeName, elementAttributeValue));
        }

        public static IList<TElement> GetAllByClass<TElement>(this IElementContainer container, string elementClass) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(Find.ByClass(elementClass));
        }

        public static IList<TElement> GetAllByText<TElement>(this IElementContainer container, string elementText) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(Find.ByText(elementText));
        }

        public static IList<TElement> GetAllByUrl<TElement>(this IElementContainer container, Uri elementUri) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(Find.ByUrl(elementUri));
        }

        public static IList<TElement> GetAllByUrl<TElement>(this IElementContainer container, string elementUrl) where TElement : Element
        {
            return container.GetAllElementsByConstraint<TElement>(Find.ByUrl(elementUrl));
        }

        public static void Click<TElement>(this IElementContainer container, Constraint constraint) where TElement : Element
        {
            var element = container.Get<TElement>(constraint, ExtensionsSettings.Assert);
            element.Click();
        }

        public static void Click<TElement>(this IElementContainer container, Constraint constraint, bool assert) where TElement : Element
        {
            var element = container.Get<TElement>(constraint, assert);
            element.Click();
        }

        public static void Click<TElement>(this IElementContainer container, string elementIdOrNameOrValueOrClassOrLabel) where TElement : Element
        {
            var element = container.Get<TElement>(elementIdOrNameOrValueOrClassOrLabel, ExtensionsSettings.Assert);
            element.Click();
        }

        public static void Click<TElement>(this IElementContainer container, string elementIdOrNameOrValueOrClassOrLabel, bool assert) where TElement : Element
        {
            var element = container.Get<TElement>(elementIdOrNameOrValueOrClassOrLabel, assert);
            element.Click();
        }

        public static void ClickByClass<TElement>(this IElementContainer container, string elementClass) where TElement : Element
        {
            var element = container.GetByClass<TElement>(elementClass, ExtensionsSettings.Assert);
            element.Click();
        }

        public static void ClickByClass<TElement>(this IElementContainer container, string elementClass, bool assert) where TElement : Element
        {
            var element = container.GetByClass<TElement>(elementClass, assert);
            element.Click();
        }

        public static void ClickByText<TElement>(this IElementContainer container, string elementText) where TElement : Element
        {
            var element = container.GetByText<TElement>(elementText, ExtensionsSettings.Assert);
            element.Click();
        }

        public static void ClickByText<TElement>(this IElementContainer container, string elementText, bool assert) where TElement : Element
        {
            var element = container.GetByText<TElement>(elementText, assert);
            element.Click();
        }

        public static void ClickByUrl<TElement>(this IElementContainer container, string url) where TElement : Element
        {
            var element = container.GetByUrl<TElement>(url, ExtensionsSettings.Assert);
            element.Click();
        }

        public static void ClickByUrl<TElement>(this IElementContainer container, string url, bool assert) where TElement : Element
        {
            var element = container.GetByUrl<TElement>(url, assert);
            element.Click();
        }

        public static void ClickByUrl<TElement>(this IElementContainer container, Uri uri) where TElement : Element
        {
            var element = container.GetByUrl<TElement>(uri, ExtensionsSettings.Assert);
            element.Click();
        }

        public static void ClickByUrl<TElement>(this IElementContainer container, Uri uri, bool assert) where TElement : Element
        {
            var element = container.GetByUrl<TElement>(uri, assert);
            element.Click();
        }

        /// <summary>
        /// Sets TextField text
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="constraint">Constraint by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void SetText(this IElementContainer container, Constraint constraint, string text, bool append = false)
        {
            var element = container.Get<TextField>(constraint);
            element.SetText(text, append);
        }

        /// <summary>
        /// Sets TextField text
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldIdOrNameOrValueOrClassOrLabel">ID/Name/Value by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void SetText(this IElementContainer container, string textFieldIdOrNameOrValueOrClassOrLabel, string text, bool append = false)
        {
            var element = container.Get<TextField>(textFieldIdOrNameOrValueOrClassOrLabel);
            element.SetText(text, append);
        }

        /// <summary>
        /// Sets TextField text
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldClass">CSS Class by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void SetTextByClass(this IElementContainer container, string textFieldClass, string text, bool append = false)
        {
            var element = container.GetByClass<TextField>(textFieldClass);
            element.SetText(text, append);
        }

        /// <summary>
        /// Sets TextField text
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldText">Text by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void SetTextByText(this IElementContainer container, string textFieldText, string text, bool append = false)
        {
            var element = container.GetByText<TextField>(textFieldText);
            element.SetText(text, append);
        }

        /// <summary>
        /// Types text in a TextField
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="constraint">Constraint by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void TypeText(this IElementContainer container, Constraint constraint, string text, bool append = false)
        {
            var element = container.Get<TextField>(constraint);
            element.TypeText(text, append);
        }

        /// <summary>
        /// Types text in a TextField
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldIdOrNameOrValueOrClassOrLabel">ID/Name/Value by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void TypeText(this IElementContainer container, string textFieldIdOrNameOrValueOrClassOrLabel, string text, bool append = false)
        {
            var element = container.Get<TextField>(textFieldIdOrNameOrValueOrClassOrLabel);
            element.TypeText(text, append);
        }

        /// <summary>
        /// Types text in a TextField
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldClass">CSS Class by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void TypeTextByClass(this IElementContainer container, string textFieldClass, string text, bool append = false)
        {
            var element = container.GetByClass<TextField>(textFieldClass);
            element.TypeText(text, append);
        }

        /// <summary>
        /// Types text in a TextField
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="textFieldText">Text by which TextField will be searched</param>
        /// <param name="text">New TextField text</param>
        /// <param name="append">(Optional) Append text?</param>
        public static void TypeTextByText(this IElementContainer container, string textFieldText, string text, bool append = false)
        {
            var element = container.GetByText<TextField>(textFieldText);
            element.TypeText(text, append);
        }

        /// <summary>
        /// Selects SelectList value
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="constraint">Constraint by which SelectList will be searched</param>
        /// <param name="value">Value of the element that will be selected</param>
        public static void SelectValue(this IElementContainer container, Constraint constraint, string value)
        {
            var element = container.Get<SelectList>(constraint);
            element.SelectByValue(value);
        }

        /// <summary>
        /// Selects SelectList value
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="selectListIdOrNameOrValueOrClassOrLabel">ID/Name/Value by which SelectList will be searched</param>
        /// <param name="value">Value of the element that will be selected</param>
        public static void SelectValue(this IElementContainer container, string selectListIdOrNameOrValueOrClassOrLabel, string value)
        {
            var element = container.Get<SelectList>(selectListIdOrNameOrValueOrClassOrLabel);
            element.SelectByValue(value);
        }

        /// <summary>
        /// Selects SelectList value
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="constraint">Constraint by which SelectList will be searched</param>
        /// <param name="text">Text of the element that will be selected</param>
        public static void SelectValueByText(this IElementContainer container, Constraint constraint, string text)
        {
            var element = container.Get<SelectList>(constraint);
            element.Select(text);
        }

        /// <summary>
        /// Selects SelectList value
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="selectListIdOrNameOrValueOrClassOrLabel">ID/Name/Value by which SelectList will be searched</param>
        /// <param name="text">Text of the element that will be selected</param>
        public static void SelectValueByText(this IElementContainer container, string selectListIdOrNameOrValueOrClassOrLabel, string text)
        {
            var element = container.Get<SelectList>(selectListIdOrNameOrValueOrClassOrLabel);
            element.Select(text);
        }

        /// <summary>
        /// Checks CheckBox
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="radioCheckIdOrNameOrValueOrClassOrLabel">ID/Name/Value by which CheckBox will be searched</param>
        /// <param name="check">New check status of the CheckBox</param>
        public static void Check(this IElementContainer container, string radioCheckIdOrNameOrValueOrClassOrLabel, bool check)
        {
            if (container.CheckBoxes.Exists(radioCheckIdOrNameOrValueOrClassOrLabel, true))
            {
                var element = container.Get<CheckBox>(radioCheckIdOrNameOrValueOrClassOrLabel);
                element.Checked = check;
                element.Change();
            }
            else
            {
                var element = container.Get<RadioButton>(radioCheckIdOrNameOrValueOrClassOrLabel);
                element.Checked = check;
                element.Change();
            }
        }

        /// <summary>
        /// Checks CheckBox or RadioButton
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="constraint">Constraint by which CheckBox will be searched</param>
        /// <param name="check">New check status of the CheckBox or RadioButton</param>
        public static void Check(this IElementContainer container, Constraint constraint, bool check)
        {
            if (container.CheckBoxes.Exists(constraint))
            {
                var element = container.Get<CheckBox>(constraint);
                element.Checked = check;
                element.Change();
            }
            else
            {
                var element = container.Get<RadioButton>(constraint);
                element.Checked = check;
                element.Change();
            }
        }

        /// <summary>
        /// Checks CheckBox or RadioButton
        /// </summary>
        /// <param name="container">Container</param>
        /// <param name="radioCheckClass">CSS Class by which CheckBox will be searched</param>
        /// <param name="check">New check status of the CheckBox or RadioButton</param>
        public static void CheckByClass(this IElementContainer container, string radioCheckClass, bool check)
        {
            if (container.CheckBoxes.Exists(radioCheckClass, true))
            {
                var element = container.GetByClass<CheckBox>(radioCheckClass);
                element.Checked = check;
                element.Change();
            }
            else
            {
                var element = container.GetByClass<RadioButton>(radioCheckClass);
                element.Checked = check;
                element.Change();
            }
        }

        private static TElement GetElementByConstraint<TElement>(this IElementContainer container, Constraint constraint) where TElement : Element
        {
            return container.ElementOfType<TElement>(constraint);
        }

        private static IList<TElement> GetAllElementsByConstraint<TElement>(this IElementContainer container, Constraint constraint) where TElement : Element
        {
            return container.ElementsOfType<TElement>().Where(x => x.Matches(constraint)).ToList();
        }
    }
}
